﻿using System.Collections;
using System.Diagnostics;
using DimensionsHelper.LanguageService.Protocol;

namespace DimensionsHelper.LanguageService.Syntax;

public class MacroParameterList : SourceNode, IReadOnlyList<MacroParameter>
{
    private SourceNode? _leftParenthesisToken;
    private SyntaxList<MacroParameter>? _parameters;
    private Dictionary<string, MacroParameter>? _parametersMap;
    private SourceNode? _rightParenthesisToken;

    internal MacroParameterList(SourceNode? parent) :
        base(parent, SyntaxKinds.MacroParameterList)
    {
    }


    public SourceNode? LeftParenthesisToken => _leftParenthesisToken;

    public SourceNode? RightParenthesisToken => _rightParenthesisToken;


    public MacroParameter this[int index] =>
        _parameters == null
            ? throw new IndexOutOfRangeException(nameof(index))
            : _parameters[index];

    public int Count => _parameters?.Count ?? 0;


    public IEnumerator<MacroParameter> GetEnumerator()
    {
        return _parameters == null
            ? Enumerable.Empty<MacroParameter>().GetEnumerator()
            : _parameters.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }


    internal override void SetUri(DocumentUri uri)
    {
        base.SetUri(uri);
        _parameters?.SetUri(uri);
    }


    public MacroParameterList Update(SourceNode? leftParen, SourceNode? rightParen)
    {
        Debug.Assert(leftParen == null || leftParen.IsNode(SyntaxKinds.LeftParenthesisToken));
        Debug.Assert(rightParen == null || rightParen.IsNode(SyntaxKinds.RightParenthesisToken));
        SetChildNullableField(ref _leftParenthesisToken, leftParen);
        SetChildNullableField(ref _rightParenthesisToken, rightParen);
        return this;
    }


    public bool Add(MacroParameter parameter)
    {
        _parameters ??= new SyntaxList<MacroParameter>();
        _parameters.Add(parameter);
        _parametersMap ??= new Dictionary<string, MacroParameter>();
        return _parametersMap.TryAdd(parameter.Name, parameter);
    }


    public MacroParameter? Find(string name)
    {
        return _parametersMap?.GetValueOrDefault(name);
    }


    public MacroParameter? First()
    {
        return _parameters == null || _parameters.Width == 0 ? null : _parameters[0];
    }


    public MacroParameter? Last()
    {
        return _parameters == null || _parameters.Width == 0 ? null : _parameters[^1];
    }


    public override void AcceptVisitor<T>(SyntaxNodeVisitor<T> visitor)
    {
        AcceptList(visitor, _parameters);
    }


    public void ForEach(Action<MacroParameter> callback)
    {
        if (_parameters != null)
        {
            foreach (MacroParameter? node in _parameters)
            {
                callback(node);
            }
        }
    }
}


public static partial class NodeFactory
{
    public static MacroParameterList CreateMacroParameterList(SourceNode? parent,
        IEnumerable<MacroParameter>? parameters)
    {
        MacroParameterList list = new(parent);

        if (parameters != null)
        {
            foreach (MacroParameter p in parameters)
            {
                list.Add(p);
            }
        }

        return list;
    }
}